classdef CodeTestingService < handle
    % CODETESTINGSERVICE 代码测试服务
    % 基于DO-178规则第4条（架构分离原则）设计
    % 负责协调所有代码测试相关的业务逻辑（SIL/PIL）
    %
    % 功能：
    %   - 代码需求验证测试（verifyObjCode2Reqs - SIL/PIL HLR测试）
    %   - 代码低级测试（verifyObjCode2LowLevelTests - SIL/PIL SLDV测试）
    %   - 合并代码覆盖率（mergeCodeCoverage - SIL/PIL）
    %
    % 设计原则：
    %   - 单一职责：只负责代码测试业务逻辑协调
    %   - 依赖注入：依赖现有的工具函数
    %   - 错误明确：遵循规则第2条，不使用silent fallback
    %   - 可测试：所有公共方法都可以独立测试
    %
    % 使用示例：
    %   % 获取单例实例（DEV环境）
    %   service = CodeTestingService.getInstance();
    %   
    %   % 获取单例实例（CI环境）
    %   service = CodeTestingService.getInstance('Environment', 'CI');
    %   
    %   % 运行SIL HLR测试
    %   result = service.verifyAgainstRequirements('MyModel', 'SIL');
    %   result = service.verifyAgainstRequirements('MyModel', 'SIL', 'DisableCoverage', 'TreatAsTopMdl', 'AuthorName');
    %   
    %   % 运行PIL低级测试
    %   result = service.verifyAgainstLowLevelTests('MyModel', 'PIL');
    %   
    %   % 合并SIL覆盖率
    %   result = service.mergeCoverage('MyModel', 'SIL');
    %
    % 作者: MBD Platform Team
    % 日期: 2025-10-26
    % 遵循DO-178标准
    
    properties (Access = private)
        projectRoot         % 项目根目录
        environment         % 运行环境：'DEV' 或 'CI'
        logger              % LoggingService实例
    end
    
    methods (Access = private)
        function obj = CodeTestingService(varargin)
            % 私有构造函数 - 初始化服务（单例模式）
            % 输入：
            %   'Environment' - 可选，运行环境 'DEV'（默认）或 'CI'
            % 基于规则第2条：不使用silent fallback，明确报告错误
            
            % 初始化日志服务
            try
                obj.logger = LoggingService.getInstance();
            catch ME
                warning('LoggingService:Unavailable', '日志服务不可用: %s', ME.message);
                obj.logger = [];
            end
            
            % 解析输入参数
            p = inputParser;
            addParameter(p, 'Environment', 'DEV', @(x) ismember(x, {'DEV', 'CI'}));
            parse(p, varargin{:});
            
            obj.environment = p.Results.Environment;
            
            try
                % 获取项目根目录
                obj.projectRoot = pwd;
                fprintf('✅ CodeTestingService已初始化\n');
                fprintf('   项目根目录: %s\n', obj.projectRoot);
                fprintf('   运行环境: %s\n', obj.environment);
                
                if ~isempty(obj.logger)
                    obj.logger.logInfo(sprintf('CodeTestingService已初始化 (环境: %s)', obj.environment));
                end
            catch ME
                if ~isempty(obj.logger)
                    obj.logger.logError(sprintf('代码测试服务初始化失败: %s', ME.message));
                end
                error('CodeTestingService:InitFailed', ...
                    '无法初始化代码测试服务：%s', ME.message);
            end
        end
    end
    
    methods (Static, Access = public)
        function obj = getInstance(varargin)
            % 获取CodeTestingService的单例实例
            % 输入：
            %   'Environment' - 可选，运行环境 'DEV'（默认）或 'CI'
            % 输出：
            %   obj - CodeTestingService单例实例
            
            persistent instance;
            
            if isempty(instance) || ~isvalid(instance)
                instance = CodeTestingService(varargin{:});
                fprintf('✅ 创建CodeTestingService单例实例\n');
            end
            
            obj = instance;
        end
    end
    
    methods (Access = public)
        
        %% ==================== 代码需求验证测试（SIL/PIL HLR）====================
        
        function result = verifyAgainstRequirements(obj, modelName, mode, disableCoverage, treatAsTopMdl, authorNames)
            % 验证代码对需求的符合性（SIL/PIL HLR测试）
            % 输入：
            %   modelName - 模型名称
            %   mode - 测试模式：'SIL' 或 'PIL'
            %   disableCoverage - (可选) 'DisableCoverage' 或空
            %   treatAsTopMdl - (可选) 'TreatAsTopMdl' 或空
            %   authorNames - (可选) 作者姓名
            % 输出：
            %   result - 包含成功标志和消息的结构体
            % 基于规则第2条：明确报告错误
            
            result = struct('success', false, 'message', '', 'reportPath', '', 'details', struct());
            
            if nargin < 3 || isempty(modelName) || isempty(mode)
                error('CodeTestingService:InvalidInput', '模型名称和测试模式不能为空');
            end
            
            % 验证测试模式
            if ~ismember(upper(mode), {'SIL', 'PIL'})
                error('CodeTestingService:InvalidMode', '测试模式必须是 SIL 或 PIL');
            end
            
            % 处理可选参数
            if nargin < 4
                disableCoverage = [];
            end
            if nargin < 5
                treatAsTopMdl = [];
            end
            if nargin < 6
                authorNames = [];
            end
            
            if ~isempty(obj.logger)
                obj.logger.logInfo(sprintf('开始运行 %s 代码需求验证测试（HLR）: %s', mode, modelName));
            end
            
            try
                fprintf('🧪 正在运行 %s 代码需求验证测试（HLR）: %s\n', mode, modelName);
                
                % 基于规则第4条：调用独立的代码需求验证工具函数
                % 根据环境添加CI参数
                if strcmpi(obj.environment, 'CI')
                    testResult = verifyObjCode2Reqs(modelName, mode, disableCoverage, treatAsTopMdl, authorNames, 'CI');
                else
                    testResult = verifyObjCode2Reqs(modelName, mode, disableCoverage, treatAsTopMdl, authorNames);
                end
                
                result.success = true;
                result.message = testResult.Message;
                result.details = testResult;
                result.reportPath = obj.getCodeHLRTestReportPath(modelName, mode);
                
                fprintf('✅ %s 代码需求验证测试完成\n', mode);
                if ~isempty(obj.logger)
                    obj.logger.logInfo(sprintf('%s 代码需求验证测试完成: %s', mode, modelName));
                end
                
            catch ME
                result.success = false;
                result.message = sprintf('%s 代码需求验证测试失败: %s', mode, ME.message);
                result.error = ME;
                if ~isempty(obj.logger)
                    obj.logger.logError(sprintf('%s 代码需求验证测试失败 [%s]: %s', mode, modelName, ME.message));
                end
                error('CodeTestingService:VerifyAgainstRequirementsFailed', result.message);
            end
        end
        
        %% ==================== 代码低级测试验证（SIL/PIL LLR）====================
        
        function result = verifyAgainstLowLevelTests(obj, modelName, mode, disableCoverage, treatAsTopMdl, authorNames)
            % 验证代码对低级测试的符合性（SIL/PIL SLDV测试）
            % 输入：
            %   modelName - 模型名称
            %   mode - 测试模式：'SIL' 或 'PIL'
            %   disableCoverage - (可选) 'DisableCoverage' 或空
            %   treatAsTopMdl - (可选) 'TreatAsTopMdl' 或空
            %   authorNames - (可选) 作者姓名
            % 输出：
            %   result - 包含成功标志和消息的结构体
            
            result = struct('success', false, 'message', '', 'reportPath', '', 'details', struct());
            
            if nargin < 3 || isempty(modelName) || isempty(mode)
                error('CodeTestingService:InvalidInput', '模型名称和测试模式不能为空');
            end
            
            % 验证测试模式
            if ~ismember(upper(mode), {'SIL', 'PIL'})
                error('CodeTestingService:InvalidMode', '测试模式必须是 SIL 或 PIL');
            end
            
            % 处理可选参数
            if nargin < 4
                disableCoverage = [];
            end
            if nargin < 5
                treatAsTopMdl = [];
            end
            if nargin < 6
                authorNames = [];
            end
            
            try
                fprintf('🧪 正在运行 %s 代码低级测试（SLDV）: %s\n', mode, modelName);
                
                % 基于规则第4条：调用独立的代码低级测试验证工具函数
                % 根据环境添加CI参数
                if strcmpi(obj.environment, 'CI')
                    testResult = verifyObjCode2LowLevelTests(modelName, mode, disableCoverage, treatAsTopMdl, authorNames, 'CI');
                else
                    testResult = verifyObjCode2LowLevelTests(modelName, mode, disableCoverage, treatAsTopMdl, authorNames);
                end
                
                result.success = true;
                result.message = testResult.Message;
                result.details = testResult;
                result.reportPath = obj.getCodeLLRTestReportPath(modelName, mode);
                
                fprintf('✅ %s 代码低级测试完成\n', mode);
                
            catch ME
                result.success = false;
                result.message = sprintf('%s 代码低级测试失败: %s', mode, ME.message);
                result.error = ME;
                error('CodeTestingService:VerifyAgainstLowLevelTestsFailed', result.message);
            end
        end
        
        %% ==================== 合并代码覆盖率 ====================
        
        function result = mergeCoverage(obj, modelName, mode)
            % 合并代码覆盖率（SIL/PIL HLR + LLR）
            % 输入：
            %   modelName - 模型名称
            %   mode - 测试模式：'SIL' 或 'PIL'
            % 输出：
            %   result - 包含成功标志、消息和覆盖率数据的结构体
            
            result = struct('success', false, 'message', '', 'reportPath', '', 'details', struct());
            
            if nargin < 3 || isempty(modelName) || isempty(mode)
                error('CodeTestingService:InvalidInput', '模型名称和测试模式不能为空');
            end
            
            % 验证测试模式
            if ~ismember(upper(mode), {'SIL', 'PIL'})
                error('CodeTestingService:InvalidMode', '测试模式必须是 SIL 或 PIL');
            end
            
            try
                fprintf('📊 正在合并 %s 代码覆盖率: %s\n', mode, modelName);
                
                % 基于规则第4条：调用独立的代码覆盖率合并工具函数
                % 根据环境添加CI参数
                if strcmpi(obj.environment, 'CI')
                    covResult = mergeCodeCoverage(modelName, mode, 'CI');
                else
                    covResult = mergeCodeCoverage(modelName, mode);
                end
                
                result.success = true;
                result.message = covResult.Message;
                result.details = covResult;
                result.reportPath = obj.getCodeCoverageReportPath(modelName, mode);
                
                fprintf('✅ %s 代码覆盖率合并完成\n', mode);
                
            catch ME
                result.success = false;
                result.message = sprintf('%s 代码覆盖率合并失败: %s', mode, ME.message);
                result.error = ME;
                error('CodeTestingService:MergeCoverageFailed', result.message);
            end
        end
        
        %% ==================== 批量测试（SIL 或 PIL）====================
        
        function results = runAllTests(obj, modelName, mode, varargin)
            % 运行指定模式的所有代码测试（HLR测试 + LLR测试 + 覆盖率合并）
            % 输入：
            %   modelName - 模型名称
            %   mode - 测试模式：'SIL' 或 'PIL'
            %   varargin - 可选参数，传递给各个测试函数
            % 输出：
            %   results - 包含所有测试结果的结构体
            % 基于规则第2条和第6条：继续执行并收集所有错误，便于调试
            
            results = struct();
            results.modelName = modelName;
            results.mode = mode;
            results.startTime = datetime('now');
            results.tests = struct();
            results.overallSuccess = true;
            
            if nargin < 3 || isempty(modelName) || isempty(mode)
                error('CodeTestingService:InvalidInput', '模型名称和测试模式不能为空');
            end
            
            % 验证测试模式
            if ~ismember(upper(mode), {'SIL', 'PIL'})
                error('CodeTestingService:InvalidMode', '测试模式必须是 SIL 或 PIL');
            end
            
            fprintf('🚀 正在运行所有 %s 代码测试: %s\n', mode, modelName);
            fprintf('═══════════════════════════════════════════\n');
            
            % 1. HLR测试
            fprintf('\n[1/3] %s HLR需求验证测试...\n', mode);
            try
                results.tests.hlrTest = obj.verifyAgainstRequirements(modelName, mode, [], [], varargin{:});
            catch ME
                results.tests.hlrTest = struct('success', false, ...
                    'message', sprintf('%s HLR测试失败: %s', mode, ME.message), ...
                    'error', ME);
                results.overallSuccess = false;
                fprintf('❌ %s HLR测试失败: %s\n', mode, ME.message);
            end
            
            % 2. LLR测试
            fprintf('\n[2/3] %s LLR低级测试...\n', mode);
            try
                results.tests.llrTest = obj.verifyAgainstLowLevelTests(modelName, mode, [], [], varargin{:});
            catch ME
                results.tests.llrTest = struct('success', false, ...
                    'message', sprintf('%s LLR测试失败: %s', mode, ME.message), ...
                    'error', ME);
                results.overallSuccess = false;
                fprintf('❌ %s LLR测试失败: %s\n', mode, ME.message);
            end
            
            % 3. 合并覆盖率
            fprintf('\n[3/3] 合并 %s 代码覆盖率...\n', mode);
            try
                results.tests.coverage = obj.mergeCoverage(modelName, mode);
            catch ME
                results.tests.coverage = struct('success', false, ...
                    'message', sprintf('%s 覆盖率合并失败: %s', mode, ME.message), ...
                    'error', ME);
                results.overallSuccess = false;
                fprintf('❌ %s 覆盖率合并失败: %s\n', mode, ME.message);
            end
            
            results.endTime = datetime('now');
            results.duration = results.endTime - results.startTime;
            
            fprintf('\n═══════════════════════════════════════════\n');
            if results.overallSuccess
                fprintf('✅ 所有 %s 代码测试完成！\n', mode);
            else
                fprintf('⚠️  部分 %s 测试失败，请查看详细结果\n', mode);
            end
            fprintf('总耗时: %s\n', char(results.duration));
            
            % 打印摘要
            obj.printTestSummary(results);
        end
        
        %% ==================== 报告路径获取 ====================
        
        function path = getCodeHLRTestReportPath(obj, modelName, mode)
            % 获取代码HLR测试报告路径
            % 基于规则第4条：使用ProjectPathManager和ProjectNamingManager
            % 基于规则第3条：使用getRptPath()从配置文件读取格式
            try
                if strcmpi(mode, 'SIL')
                    path = getRptPath('HLR SIL report', modelName);
                elseif strcmpi(mode, 'PIL')
                    path = getRptPath('HLR PIL report', modelName);
                else
                    error('不支持的测试模式: %s', mode);
                end
            catch ME
                % 基于规则第2条：明确报告错误，不使用silent fallback
                error('CodeTestingService:GetPathFailed', ...
                    '无法获取 %s HLR测试报告路径: %s', mode, ME.message);
            end
        end
        
        function path = getCodeLLRTestReportPath(obj, modelName, mode)
            % 获取代码LLR测试报告路径
            % 基于规则第4条：使用ProjectPathManager和ProjectNamingManager
            % 基于规则第3条：使用getRptPath()从配置文件读取格式
            try
                if strcmpi(mode, 'SIL')
                    path = getRptPath('LLR SIL report', modelName);
                elseif strcmpi(mode, 'PIL')
                    path = getRptPath('LLR PIL report', modelName);
                else
                    error('不支持的测试模式: %s', mode);
                end
            catch ME
                % 基于规则第2条：明确报告错误，不使用silent fallback
                error('CodeTestingService:GetPathFailed', ...
                    '无法获取 %s LLR测试报告路径: %s', mode, ME.message);
            end
        end
        
        function path = getCodeCoverageReportPath(obj, modelName, mode)
            % 获取代码覆盖率报告路径
            % 基于规则第4条：使用ProjectPathManager和ProjectNamingManager
            try
                if strcmpi(mode, 'SIL')
                    dirPath = ProjectPathManager.getDirPath('SIL coverage', modelName);
                elseif strcmpi(mode, 'PIL')
                    dirPath = ProjectPathManager.getDirPath('PIL coverage', modelName);
                else
                    error('不支持的测试模式: %s', mode);
                end
                % 代码覆盖率报告固定名称
                path = fullfile(dirPath, [modelName, '_Merged_Code_Coverage_Report.html']);
            catch ME
                % 基于规则第2条：明确报告错误，不使用silent fallback
                error('CodeTestingService:GetPathFailed', ...
                    '无法获取 %s 代码覆盖率报告路径: %s', mode, ME.message);
            end
        end
        
        %% ==================== 辅助方法 ====================
        
        function printTestSummary(obj, results)
            % 打印测试结果摘要
            % 输入：
            %   results - runAllTests返回的结果结构体
            
            fprintf('\n📊 测试结果摘要\n');
            fprintf('═══════════════════════════════════════════\n');
            
            testNames = fieldnames(results.tests);
            for i = 1:length(testNames)
                testName = testNames{i};
                testResult = results.tests.(testName);
                
                % 格式化测试名称
                switch testName
                    case 'hlrTest'
                        displayName = sprintf('%s HLR需求验证测试', results.mode);
                    case 'llrTest'
                        displayName = sprintf('%s LLR低级测试', results.mode);
                    case 'coverage'
                        displayName = sprintf('%s 代码覆盖率合并', results.mode);
                    otherwise
                        displayName = testName;
                end
                
                % 打印结果
                if testResult.success
                    fprintf('  ✅ %s: 成功\n', displayName);
                else
                    fprintf('  ❌ %s: 失败\n', displayName);
                end
            end
            
            fprintf('═══════════════════════════════════════════\n');
        end
    end
end

